Compound Components
2つ以上のComponentを一緒に使うことを表現する際に使う
1つの親と、複数の子という関係になる
利用者から見れば、どのComponent同士を一緒に使うべきかが明確になる
それら複数のComponentの中で状態を扱う時に便利
その状態を利用者に表出させずに扱える
1つのimoprtで済む
こういうやつ
code:jsx
<Tab>
<Tab.Item>あいてむ1</Tab.Item>
<Tab.Item>あいてむ2</Tab.Item>
<Tab.Item>あいてむ3</Tab.Item>
</Tab>
定義の仕方
こんな関数を用意して
code:ts
const compound = <
Root extends React.FC<any>,
(
root: Root,
children: Children,
): Root & Children => {
return Object.assign(root, children) as any;
};
使う
code:ts
export const FormLayout = compound(FormLayoutRoot, { Group, Group2 });
例
<Tab>と<Tab.Item>とか
<Select>と<Select.Option>とか
<Menu>
<Menu.Button>
<Menu.Items>
<Menu.Item>
参考
useContextを使っている
Parent.Childという形にはなっていないが、例としてわかりやすい
useContextと相性が良い
recoilだとglobalに影響が出る
同じ構造のものを使う時にfamilyを意識しないといけなくなる
子要素を複数置けるとか
これだけなら、listで受け取れば良いだけか
これはrender props patternでもできる
状態を共有できる
これはrender props patternでもできる
配置する場所を自由に指定できる
ここが大きな違いだと思うmrsekut.icon
ShopifyのPoralisのStack
code:ts
// item.tsx
export function Item({children, fill}: ItemProps) {
const className = classNames(styles.Item, fill && styles'Item-fill'); return <div className={className}>{children}</div>;
}
// stack.tsx
export const Stack = memo(function Stack({
children,
vertical,
spacing,
distribution,
alignment,
wrap,
}: StackProps) {
const className = classNames(
styles.Stack,
vertical && styles.vertical,
wrap === false && styles.noWrap,
);
const itemMarkup = elementChildren(children).map((child, index) => {
const props = {key: index};
return wrapWithComponent(child, Item, props);
});
return <div className={className}>{itemMarkup}</div>;
}) as NamedExoticComponent<StackProps> & {
Item: typeof Item;
};
Stack.Item = Item;
elementChildren
wrapWithComponent